package cn.fanqinghong.ymcc.service.impl;

import cn.fanqinghong.ymcc.constants.KillCourseConstant;
import cn.fanqinghong.ymcc.domain.Course;
import cn.fanqinghong.ymcc.domain.CourseMarket;
import cn.fanqinghong.ymcc.domain.KillActivity;
import cn.fanqinghong.ymcc.domain.KillCourse;
import cn.fanqinghong.ymcc.dto.KillOrderParamDto;
import cn.fanqinghong.ymcc.dto.KillPreOrderDto;
import cn.fanqinghong.ymcc.mapper.KillCourseMapper;
import cn.fanqinghong.ymcc.service.IKillActivityService;
import cn.fanqinghong.ymcc.service.IKillCourseService;
import cn.fanqinghong.ymcc.util.AssertUtil;
import cn.fanqinghong.ymcc.util.CodeGenerateUtils;
import cn.fanqinghong.ymcc.vo.CourseItemVo;
import cn.fanqinghong.ymcc.vo.CourseOrderVo;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author superman.fan
 * @since 2022-08-04
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private IKillActivityService killActivityService;
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    /**
     * 上架秒杀课程
     * @param killCourse
     */
    @Override
    public void save(KillCourse killCourse) {
        //同一个课程不能加入两次秒杀
        KillCourse killCourseTmp = selectActivityIdAndCourseId(killCourse.getActivityId(), killCourse.getCourseId());
        AssertUtil.isNull(killCourseTmp, KillCourseConstant.COURSE_IN_ACTIVITY);
        KillActivity killActivity = killActivityService.selectById(killCourse.getActivityId());
        killCourse.setCreateTime(new Date());
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setStartTime(killActivity.getStartTime());
        killCourse.setEndTime(killActivity.getEndTime());
        insert(killCourse);
    }

    /**
     * 查询redis中的所有活动课程
     * @param
     * @return
     */
    @Override
    public List<KillCourse> onlineAll() {
        Set<Object> keys = redisTemplate.keys("activity:*");
        List<KillCourse> allKillCourse = new ArrayList<>();
        keys.forEach(key ->{
            List values = redisTemplate.opsForHash().values(key);
            allKillCourse.addAll(values);
        });

        return allKillCourse;
    }

    /**
     * 查询秒杀课程
     * @param activityId
     * @param killId
     * @return
     */
    @Override
    public KillCourse onlineOne(Long activityId, Long killId) {

        return (KillCourse)redisTemplate.opsForHash().get(KillCourseConstant.ACTIVITY_PRE+activityId,killId.toString());
    }

    /**
     * 1.查询秒杀课程
     * 2.扣减信号量
     * 3.秒杀失败，扣减心好累失败
     * 4.扣减信号量成功，秒杀成功，生成订单，返回订单号
     * @param dto
     * @return
     */
    @Override
    public String kill(KillOrderParamDto dto) {
        Long activityId = dto.getActivityId();
        Long killCourseId = dto.getKillCourseId();
        //1.查询秒杀课程
        KillCourse killCourse = onlineOne(activityId, killCourseId);
        AssertUtil.isNotNull(killCourse,"秒杀课程失败");
        AssertUtil.isTrue(killCourse.isKilling(),"课程不在秒杀中");
        //判断登陆人
        Long loginId = 3l;

        //判断改用户是否已经秒杀成功过
        String repeatKey = activityId + ":" + killCourseId + ":" +loginId;
        Object o = redisTemplate.opsForValue().get(repeatKey);
        AssertUtil.isNull(o,"课程只能秒杀一次");

        //2.扣减信号量
        //设置key 活动id：当前秒杀课程的活动id
        String key =  activityId + ":" + killCourseId;
        //3.设置信号量,课程的秒杀量
        RSemaphore semaphore = redissonClient.getSemaphore(key);
        //@todo设置秒杀量，应该是前端选择数量
        Integer killCount = 1;
        boolean reduceSemaphore = semaphore.tryAcquire(killCount);
        AssertUtil.isTrue(reduceSemaphore,"秒杀失败，快去练练手速，下次见！！！");

        //4.扣减信号量成功，秒杀成功，生成预创建订单，返回订单号
        //创建一个唯一的id，订单的id,使用当前时间拼接随机串,设置订单号
        SimpleDateFormat simple = new SimpleDateFormat("yyyyMMddHHmmss");
        simple.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        String now5 = simple.format(new Date());
        String orderNo = now5 + CodeGenerateUtils.generateOrderSn(loginId);
        KillPreOrderDto killPreOrderDto = new KillPreOrderDto(
                orderNo,
                killCount,
                killCourse.getKillPrice(),
                loginId,
                killCourse.getCourseId(),
                killCourse.getCourseName(),
                killCourse.getCoursePic(),
                activityId,
                killCourseId
        );
        //保存预创订单到redis
        redisTemplate.opsForValue().set(orderNo,killPreOrderDto);
        //存储秒杀成功的信息，防止多次秒杀
        redisTemplate.opsForValue().set(repeatKey, killPreOrderDto.getOrderNo());
        return orderNo;
    }

    /**
     * 秒杀订单的回显
     * @param orderNo
     * @return
     */
    @Override
    public CourseOrderVo oneByOrder(String orderNo) {
        CourseOrderVo courseOrderVo = new CourseOrderVo();
        CourseItemVo courseItemVo = new CourseItemVo();
        ArrayList<CourseItemVo> courseItemVos = new ArrayList<>();
        //通过订单号查询预创订单
        KillPreOrderDto proOrder = (KillPreOrderDto)redisTemplate.opsForValue().get(orderNo);
        courseOrderVo.setTotalAmount(proOrder.getKillAmount());
        Course course = new Course();
        course.setPic(proOrder.getPic());
        course.setLoginId(proOrder.getLoginId());
        course.setName(proOrder.getCourseName());
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setPrice(proOrder.getKillAmount());
        courseItemVo.setCourse(course);
        courseItemVo.setCourseMarket(courseMarket);
        courseItemVos.add(courseItemVo);
        courseOrderVo.setCourseInfos(courseItemVos);

        return courseOrderVo;
    }

    /**
     * 根据课程id和活动id查询数据
     * @param activityId
     * @param courseId
     * @return
     */
    public KillCourse selectActivityIdAndCourseId(Long activityId , Long courseId){
        Wrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("course_id",courseId);
        wrapper.eq("activity_id",activityId);
        return selectOne(wrapper);
    }

}
